<html>
<head></head>
<body><a name="ZH-CN_TOPIC_0000002313462933"></a><a name="ZH-CN_TOPIC_0000002313462933"></a>
<h1>不依赖UI组件的全局自定义弹出框 (openCustomDialog)（推荐）</h1>
<div><p>推荐使用UIContext中获取到的PromptAction对象提供的<a
        href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-uicontext#opencustomdialog12"
        target="_blank">openCustomDialog</a>接口在相对应用复杂的场景来实现自定义弹出框，相较于<a
        href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-methods-custom-dialog-box#customdialogcontroller"
        target="_blank">CustomDialogController</a>优势点在于页面解耦，支持<a
        href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-componentcontent#update"
        target="_blank">动态刷新</a>。</p>
    <div class="note"><img originheight="38" originwidth="102"
                           src="https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20250709163041.72910430474788418119192633088483:50001231000000:2800:6A2FC16049CA8852AE19B58D4B7054C0CB0E04DEBDD0333C58EA5ED3305DA0FA.png"><span
            class="notetitle"> </span>
        <div class="notebody"><p>弹出框（openCustomDialog）存在两种入参方式创建自定义弹出框：</p>
            <ul>
                <li>
                    openCustomDialog（传参为ComponentContent形式）：通过ComponentContent封装内容可以与UI界面解耦，调用更加灵活，可以满足开发者的封装诉求。具有较高的灵活性，弹出框样式完全自定义，并且在弹出框打开后可以使用updateCustomDialog方法动态更新弹出框的参数。
                </li>
                <li>
                    openCustomDialog（传builder的形式）：相对于ComponentContent，builder必须要与上下文做绑定，与UI存在一定耦合。此方法有用默认的弹出框样式，适合于开发者想要实现与系统弹窗默认风格一致的效果。
                </li>
            </ul>
            <p>本文介绍通过入参形式为ComponentContent创建自定义弹出框，传builder形式的弹出框使用方法可参考<a
                    href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-uicontext#opencustomdialog12-1"
                    target="_blank">openCustomDialog</a>。</p></div>
    </div>
    <p>弹出框（openCustomDialog）默认为模态弹窗且有蒙层，不可与蒙层下方控件进行交互（不支持点击和手势等向下透传）。可以通过配置<a
            href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-uicontext#opencustomdialog12"
            target="_blank">isModal</a>来实现模态和非模态弹窗，详细说明可参考<a
            href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-dialog-overview#弹窗的种类">弹窗的种类</a>。
    </p>
    <p>
        当isModal为true时，弹出框为模态弹窗，且弹窗周围的蒙层区不支持透传。isModal为false时，弹出框为非模态弹窗，且弹窗周围的蒙层区可以透传。因此如果需要同时允许弹出框的交互和弹出框外页面的交互行为，需要将弹出框设置为非模态。</p>
    <div class="section" id="生命周期"><a name="ZH-CN_TOPIC_0000002313462933__%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"></a><a
            name="%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"></a>     <h4>生命周期</h4>
        <p>弹出框提供了生命周期函数用于通知用户该弹出框的生命周期。生命周期的触发时序依次为：onWillAppear -&gt;
            onDidAppear -&gt; onWillDisappear -&gt; onDidDisappear。</p>
        <div class="tablenoborder">
            <table>
                <thead>
                <tr>
                    <th align="left" class="cellrowborder" id="mcps1.3.5.3.1.4.1.1" valign="top"
                        width="33.33333333333333%">名称
                    </th>
                    <th align="left" class="cellrowborder" id="mcps1.3.5.3.1.4.1.2" valign="top"
                        width="33.33333333333333%">类型
                    </th>
                    <th align="left" class="cellrowborder" id="mcps1.3.5.3.1.4.1.3" valign="top"
                        width="33.33333333333333%">说明
                    </th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">onDidAppear</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">() =&gt; void</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">弹出框弹出时的事件回调。</td>
                </tr>
                <tr>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">onDidDisappear</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">() =&gt; void</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">弹出框消失时的事件回调。</td>
                </tr>
                <tr>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">onWillAppear</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">() =&gt; void</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">弹出框显示动效前的事件回调。</td>
                </tr>
                <tr>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">onWillDisappear</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">() =&gt; void</td>
                    <td class="cellrowborder" valign="top" width="33.33333333333333%">弹出框退出动效前的事件回调。</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div class="section" id="自定义弹出框的打开与关闭"><a
            name="ZH-CN_TOPIC_0000002313462933__%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E5%87%BA%E6%A1%86%E7%9A%84%E6%89%93%E5%BC%80%E4%B8%8E%E5%85%B3%E9%97%AD"></a><a
            name="%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E5%87%BA%E6%A1%86%E7%9A%84%E6%89%93%E5%BC%80%E4%B8%8E%E5%85%B3%E9%97%AD"></a>
        <h4>自定义弹出框的打开与关闭</h4>
        <div class="note"><img originheight="38" originwidth="102"
                               src="https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20250709163041.69684985902172997274717811617915:50001231000000:2800:38E757B8940E9DB40D1F4006C46FA92D88150C1E1E95A195DBA588B3A842DFB5.png"><span
                class="notetitle"> </span>
            <div class="notebody"><p>详细变量定义请参考<a href="#完整示例">完整示例</a>。</p></div>
        </div>
        <ol>
            <li><p>创建ComponentContent。</p>
                <p>ComponentContent用于定义自定义弹出框的内容。其中，wrapBuilder(buildText)封装自定义组件，new
                    Params(this.message)是自定义组件的入参，可以缺省，也可以传入基础数据类型。</p>
                <pre class="ts">private contentNode: ComponentContent&lt;Object&gt; = new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));</pre>
            </li>
            <li><p>打开自定义弹出框。</p>
                <p>
                    调用openCustomDialog接口打开的弹出框默认customStyle为true，即弹出框的内容样式完全按照contentNode自定义样式显示。</p>
                <pre class="ts">PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
  .then(() =&gt; {
    console.info('OpenCustomDialog complete.');
  })
  .catch((error: BusinessError) =&gt; {
    let message = (error as BusinessError).message;
    let code = (error as BusinessError).code;
    console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
  })</pre>
            </li>
            <li><p>关闭自定义弹出框。</p>
                <p>
                    由于closeCustomDialog接口需要传入待关闭弹出框对应的ComponentContent。因此，如果需要在弹出框中设置关闭方法，则可参考完整示例封装静态方法来实现。</p>
                <p>关闭弹出框之后若需要释放对应的ComponentContent，则需要调用ComponentContent的<a
                        href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-componentcontent#dispose"
                        target="_blank">dispose</a>方法。</p>
                <pre class="ts">PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
  .then(() =&gt; {
    console.info('CloseCustomDialog complete.');
    if (this.contentNode !== null) {
         this.contentNode.dispose();   // 释放contentNode
     }
  })
  .catch((error: BusinessError) =&gt; {
    let message = (error as BusinessError).message;
    let code = (error as BusinessError).code;
    console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
  })</pre>
            </li>
        </ol>
    </div>
    <div class="section" id="更新自定义弹出框的内容"><a
            name="ZH-CN_TOPIC_0000002313462933__%E6%9B%B4%E6%96%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E5%87%BA%E6%A1%86%E7%9A%84%E5%86%85%E5%AE%B9"></a><a
            name="%E6%9B%B4%E6%96%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E5%87%BA%E6%A1%86%E7%9A%84%E5%86%85%E5%AE%B9"></a>
        <h4>更新自定义弹出框的内容</h4>
        <p>ComponentContent与<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-buildernode"
                target="_blank">BuilderNode</a>有相同的使用限制，不支持自定义组件使用<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-create-custom-components#自定义组件的基本结构">@Reusable</a>、<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-link">@Link</a>、<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-provide-and-consume">@Provide</a>、<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-provide-and-consume">@Consume</a>等装饰器，来同步弹出框弹出的页面与ComponentContent中自定义组件的状态。因此，若需要更新弹出框中自定义组件的内容可以通过ComponentContent提供的update方法来实现。
        </p>
        <pre class="ts">this.contentNode.update(new Params('update'))</pre>
    </div>
    <div class="section" id="更新自定义弹出框的属性"><a
            name="ZH-CN_TOPIC_0000002313462933__%E6%9B%B4%E6%96%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E5%87%BA%E6%A1%86%E7%9A%84%E5%B1%9E%E6%80%A7"></a><a
            name="%E6%9B%B4%E6%96%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E5%87%BA%E6%A1%86%E7%9A%84%E5%B1%9E%E6%80%A7"></a>
        <h4>更新自定义弹出框的属性</h4>
        <p>
            通过updateCustomDialog可以动态更新弹出框的属性。目前支持更新弹出框的对齐方式、基于对齐方式的偏移量、是否点击蒙层自动关闭以及蒙层颜色，对应的属性分别为alignment、offset、autoCancel、maskColor。</p>
        <p>更新属性时，未设置的属性会恢复为默认值。例如，初始设置{ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50
            } }，更新时设置{ alignment: DialogAlignment.Bottom }，则初始设置的offset: { dx: 0, dy: 50
            }不会保留，会恢复为默认值。</p>
        <pre class="ts">PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
  .then(() =&gt; {
    console.info('UpdateCustomDialog complete.');
  })
  .catch((error: BusinessError) =&gt; {
    let message = (error as BusinessError).message;
    let code = (error as BusinessError).code;
    console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
  })</pre>
    </div>
    <div class="section" id="为弹出框内容和蒙层设置不同的动画效果"><a
            name="ZH-CN_TOPIC_0000002313462933__%E4%B8%BA%E5%BC%B9%E5%87%BA%E6%A1%86%E5%86%85%E5%AE%B9%E5%92%8C%E8%92%99%E5%B1%82%E8%AE%BE%E7%BD%AE%E4%B8%8D%E5%90%8C%E7%9A%84%E5%8A%A8%E7%94%BB%E6%95%88%E6%9E%9C"></a><a
            name="%E4%B8%BA%E5%BC%B9%E5%87%BA%E6%A1%86%E5%86%85%E5%AE%B9%E5%92%8C%E8%92%99%E5%B1%82%E8%AE%BE%E7%BD%AE%E4%B8%8D%E5%90%8C%E7%9A%84%E5%8A%A8%E7%94%BB%E6%95%88%E6%9E%9C"></a>
        <h4>为弹出框内容和蒙层设置不同的动画效果</h4>
        <p>当弹出框出现时，内容与蒙层显示动效一致。若开发者希望为弹出框内容及蒙层设定不同动画效果，可通过<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-promptaction#basedialogoptions11"
                target="_blank">BaseDialogOptions</a>中dialogTransition和maskTransition属性单独配置弹窗内容与蒙层的动画。具体的动画效果请参考<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-transition-animation-component"
                target="_blank">组件内转场 (transition)</a>。</p>
        <div class="note"><img originheight="38" originwidth="102"
                               src="https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20250709163041.01811051688398356121037831622991:50001231000000:2800:B1A1ED5F2C0A84CA66E33A252578ECDB8EBC663B2CC6D631475DC1C1AA8D3D88.png"><span
                class="notetitle"> </span>
            <div class="notebody"><p>
                当isModal为true时，蒙层将显示，此时可以设置蒙层的动画效果；否则，maskTransition将不生效。</p></div>
        </div>
        <pre class="ts">import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  private customDialogComponentId: number = 0
  @Builder
  customDialogComponent() {
    Row({ space: 50 }) {
      Button("这是一个弹窗")
    }.height(200).padding(5)
  }

  build() {
    Row() {
      Row({ space: 20 }) {
        Text('打开弹窗')
          .fontSize(30)
          .onClick(() =&gt; {
            this.getUIContext().getPromptAction().openCustomDialog({
              builder: () =&gt; {
                this.customDialogComponent()
              },
              isModal:true,
              showInSubWindow:false,
              maskColor: Color.Pink,
              maskRect:{ x: 20, y: 20, width: '90%', height: '90%' },

              dialogTransition: // 设置弹窗内容显示的过渡效果
              TransitionEffect.translate({ x: 0, y: 290, z: 0 })
                .animation({ duration: 4000, curve: Curve.Smooth }),// 四秒钟的偏移渐变动画

              maskTransition: // 设置蒙层显示的过渡效果
              TransitionEffect.opacity(0)
                .animation({ duration: 4000, curve: Curve.Smooth }) // 四秒钟的透明渐变动画

            }).then((dialogId: number) =&gt; {
              this.customDialogComponentId = dialogId
            })
              .catch((error: BusinessError) =&gt; {
                console.error(`openCustomDialog error code is ${error.code}, message is ${error.message}`)
              })
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}</pre>
        <p><span><img originheight="740" originwidth="349"
                      src="https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20250709163041.67312925575254262310104067765313:50001231000000:2800:C663B016B9E8D8875C627DA23CBCF49E495DC2D3E405FC6A0FFF2A7B883F0078.gif"></span>
        </p></div>
    <div class="section" id="设置弹出框避让软键盘的距离"><a
            name="ZH-CN_TOPIC_0000002313462933__%E8%AE%BE%E7%BD%AE%E5%BC%B9%E5%87%BA%E6%A1%86%E9%81%BF%E8%AE%A9%E8%BD%AF%E9%94%AE%E7%9B%98%E7%9A%84%E8%B7%9D%E7%A6%BB"></a><a
            name="%E8%AE%BE%E7%BD%AE%E5%BC%B9%E5%87%BA%E6%A1%86%E9%81%BF%E8%AE%A9%E8%BD%AF%E9%94%AE%E7%9B%98%E7%9A%84%E8%B7%9D%E7%A6%BB"></a>
        <h4>设置弹出框避让软键盘的距离</h4>
        <p>为显示弹出框的独立性，弹出框弹出时会与周边进行避让，包括状态栏、导航条以及键盘等留有间距。故当软键盘弹出时，默认情况下，弹出框会自动避开软键盘，并与之保持16vp的距离。开发者可以利用<a
                href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-promptaction#basedialogoptions11"
                target="_blank">BaseDialogOptions</a>中的keyboardAvoidMode和keyboardAvoidDistance这两个配置项，来设置弹出框在软键盘弹出时的行为，包括是否需要避开软键盘以及与软键盘之间的距离。
        </p>
        <p>设置软键盘间距时，需要将keyboardAvoidMode值设为KeyboardAvoidMode.DEFAULT。</p>
        <pre class="ts">import { BusinessError } from '@kit.BasicServicesKit';
import { LengthMetrics } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @Builder
  customDialogComponent() {
      Column() {
        Text('keyboardAvoidDistance: 0vp')
          .fontSize(20)
          .margin({ bottom: 36 })
        TextInput({ placeholder: '' })
      }.backgroundColor('#FFF0F0F0')
  }

  build() {
    Row() {
      Row({ space: 20 }) {
        Text('打开弹窗')
          .fontSize(30)
          .onClick(() =&gt; {
            this.getUIContext().getPromptAction().openCustomDialog({
              builder: () =&gt; {
                this.customDialogComponent()
              },
              alignment:DialogAlignment.Bottom,
              keyboardAvoidMode: KeyboardAvoidMode.DEFAULT, // 软键盘弹出时，弹出框自动避让
              keyboardAvoidDistance: LengthMetrics.vp(0) // 软键盘弹出时与弹出框的距离为0vp
            }).catch((error: BusinessError) =&gt; {
                console.error(`openCustomDialog error code is ${error.code}, message is ${error.message}`)
              })
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}</pre>
        <p><span><img originheight="662" originwidth="400"
                      src="https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20250709163041.86529865415280846438966580107490:50001231000000:2800:B98A747B0E2289C22D66748B3D43EDC49B9C606C8969B8CDDE8DBCFC0BA55A19.gif"></span>
        </p></div>
    <div class="section" id="完整示例"><a name="ZH-CN_TOPIC_0000002313462933__%E5%AE%8C%E6%95%B4%E7%A4%BA%E4%BE%8B"></a><a
            name="%E5%AE%8C%E6%95%B4%E7%A4%BA%E4%BE%8B"></a>     <h4>完整示例</h4>
        <pre class="ts">// PromptActionClass.ets
import { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent, promptAction, UIContext } from '@kit.ArkUI';

export class PromptActionClass {
  static ctx: UIContext;
  static contentNode: ComponentContent&lt;Object&gt;;
  static options: promptAction.BaseDialogOptions;

  static setContext(context: UIContext) {
    PromptActionClass.ctx = context;
  }

  static setContentNode(node: ComponentContent&lt;Object&gt;) {
    PromptActionClass.contentNode = node;
  }

  static setOptions(options: promptAction.BaseDialogOptions) {
    PromptActionClass.options = options;
  }

  static openDialog() {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
        .then(() =&gt; {
          console.info('OpenCustomDialog complete.');
        })
        .catch((error: BusinessError) =&gt; {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }

  static closeDialog() {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
        .then(() =&gt; {
          console.info('CloseCustomDialog complete.');
        })
        .catch((error: BusinessError) =&gt; {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }

  static updateDialog(options: promptAction.BaseDialogOptions) {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
        .then(() =&gt; {
          console.info('UpdateCustomDialog complete.');
        })
        .catch((error: BusinessError) =&gt; {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }
}</pre>
        <pre class="ts">// Index.ets
import { ComponentContent } from '@kit.ArkUI';
import { PromptActionClass } from './PromptActionClass';

class Params {
  text: string = "";

  constructor(text: string) {
    this.text = text;
  }
}

@Builder
function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 36 })
    Button('Close')
      .onClick(() =&gt; {
        PromptActionClass.closeDialog();
      })
  }.backgroundColor('#FFF0F0F0')
}

@Entry
@Component
struct Index {
  @State message: string = "hello";
  private ctx: UIContext = this.getUIContext();
  private contentNode: ComponentContent&lt;Object&gt; =
    new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));

  aboutToAppear(): void {
    PromptActionClass.setContext(this.ctx);
    PromptActionClass.setContentNode(this.contentNode);
    PromptActionClass.setOptions({ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } });
  }

  build() {
    Row() {
      Column() {
        Button("open dialog and update options")
          .margin({ top: 50 })
          .onClick(() =&gt; {
            PromptActionClass.openDialog();

            setTimeout(() =&gt; {
              PromptActionClass.updateDialog({
                alignment: DialogAlignment.Bottom,
                offset: { dx: 0, dy: -50 }
              });
            }, 1500)
          })
        Button("open dialog and update content")
          .margin({ top: 50 })
          .onClick(() =&gt; {
            PromptActionClass.openDialog();

            setTimeout(() =&gt; {
              this.contentNode.update(new Params('update'));
            }, 1500)
          })
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }
}</pre>
        <p><span><img originheight="730" originwidth="352"
                      src="https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20250709163041.20335248928800702200590630912686:50001231000000:2800:5111E15E806CA97F79DCD49818ECA9E3B9919A589C694E24329FB0999230960A.gif"></span>
        </p></div>
</div>
<div></div>
</body>
</html>